function Activity(){
    this.serverPayRank = {
        rank : {},
        time : 0,
    };

    this.updates = {};
}

Activity.create = function() {
    var initActivity = {
        _id : 'activity',
        'server_pay_rank' : {   // 跨服充值排行
            'rank': {},         // 跨服排行
            'time': 0,          // 活动开启时间
        },
    };
    gDBWorld.insert(initActivity, function(err, result){}); 
};

Activity.prototype = {
    init : function(callback) {
        gDBWorld.findOne({_id : 'activity'}, {}, function(err, doc){
            if( doc ) {
                this.serverPayRank = doc.server_pay_rank;

                this.save();
                callback && callback(true);
            }else{
                callback && callback(false);
            }
        }.bind(this));
    },

    addUpdate : function(item, obj) {
        this.updates[item] = obj;
    },

    save : function(callback) {
        if( !this.updates || Object.keys(this.updates).length == 0 ) {
            callback && callback(true);
            return;
        }

        var updates = this.updates;
        gDBWorld.update({_id : 'activity'}, {$set:updates}, function(err, result){
            if( err ) {
                ERROR({updates:updates, err:err});
                callback && callback(false);
            }else {
                callback && callback(true);
            }
        });

        this.updates = {};
    },

    updateServerPayRankActivity : function(time) {
        if( time == this.serverPayRank.time ) {
            return;
        }

        this.serverPayRank.rank = {};
        this.serverPayRank.time = time;

        this.addUpdate('server_pay_rank', this.serverPayRank);
    },

    updateServerPayRank : function(uid, server, paid, name) {
        var serverGroup = Math.ceil(server/10);

        if( !this.serverPayRank.rank[serverGroup] ) {
            this.serverPayRank.rank[serverGroup] = [];
        }

        var payRank = this.serverPayRank.rank[serverGroup];

        var minPaid = 0;
        if( payRank.length > 0 ) {
            minPaid = payRank[payRank.length-1].paid;
        }

        if( payRank.length >= 10 && paid <= minPaid ) {
            return;
        }
        
        var userName = name || '';

        var inRank = false;
        for( var i=0; i<payRank.length; i++ ) {
            var rank = payRank[i];
            if( rank.uid == uid ) {
                rank.paid = paid;
                if( userName ) {
                    rank.name = userName;
                }
                inRank = true;
                break;
            }
        }

        if( !inRank ) {
            payRank.push({'uid':uid, 'paid':paid, 'name':userName, 'rank':1});
        }

        payRank.sort(function(a, b){
            return b.paid - a.paid; 
        });

        var rankCursor = 0; // 遍历payRank列表游标
        for( var i=1; i<=5; i++ ) {
            var rankConf = gConfAvServerPayRank[i];
            var startRank = 1;
            if( i == 1) {
                startRank = rankConf.Rank;
            }else{
                startRank = gConfAvServerPayRank[i-1].Rank + 1;
            }
            var endRank = rankConf.Rank;
            var payMin = rankConf.PayMin;

            for( var j=startRank; j<=endRank; j++ ) {
                var rankData = payRank[rankCursor];
                if( !rankData ) break;

                if( rankData.paid >= payMin ) {
                    rankData.id = i;
                    rankData.rank = j;
                    rankCursor += 1;
                }
            }
        }

        var lastRank = Math.max(payRank[rankCursor-1].rank, gConfAvServerPayRank[5].Rank);
        for( var k=rankCursor; k<payRank.length; k++ ) {
            lastRank += 1;
            var rankData = payRank[k];
            rankData.rank = lastRank;
            delete rankData.id;
        }

        this.addUpdate('server_pay_rank', this.serverPayRank);
    },
};

exports.get_server_payrank = function(req, res, resp) {
    gActivity.updateServerPayRankActivity(req.args.time);
    var payRank = gActivity.serverPayRank.rank[Math.ceil(req.args.server/10)] || [];

    var maxRank = Math.min(10, payRank.length);
    var i= 0;
    for( ; i<maxRank ;i++ ) {
        if( payRank[i].rank >= 10 ) {
            break;
        }
    }
    
    var rank = 0;
    for( var j=0; j<payRank.length; j++ ) {
        var rankData = payRank[j];
        if(rankData.uid == req.uid && rankData.id) {
            rank = rankData.rank;
            break;
        }
    }

    resp.data.pay_rank = payRank.slice(0, i+1);
    resp.data.rank = rank;
    onReqHandled(res, resp);
};

exports.server_payrank_top_reward = function(req, res, resp) {
    var payRank = gActivity.serverPayRank.rank[Math.ceil(req.args.server/10)] || [];

    var maxRank = Math.min(10, payRank.length);
    var rankData = null;
    for( var i=0; i<payRank.length ;i++ ) {
        var rank = payRank[i];
        if( !rank.id ) break;

        if( rank.uid == req.uid ) {
            rankData = rank;
            break;
        }
    }

    if( !rankData || !rankData.id ) {
        resp.code = 1; resp.desc = 'not in top10';
    }else if( rankData.reward ) {
        resp.code = 1; resp.desc = 'got';
    }else{
        resp.data.rank = rankData.id;
        rankData.reward = 1;
        gActivity.addUpdate('server_pay_rank', gActivity.serverPayRank);
        gActivity.save();
    }

    onReqHandled(res, resp);
};

exports.update_server_payrank = function(req, res, resp) {
    do {
        gActivity.updateServerPayRankActivity(req.args.time);
        gActivity.updateServerPayRank(req.uid, req.args.server, req.args.paid, req.args.name);

        gActivity.save();

    }while(false);

    onReqHandled(res, resp);
};

exports.server_payrank_reward = function(req, res, resp) {
    do {
        var rewardRank = gConfAvServerPayRank[req.args.id].Rank;
    
        var payRank = gActivity.serverPayRank.rank[Math.ceil(req.args.server/10)] || [];

        var isReach = false;
        for( var i=0; i<payRank.length ;i++ ) {
            var rankData = payRank[i];
            if( !rankData ) continue;
            if( +rankData.rank == rewardRank ) {
                isReach = true;
                break;
            }
        }

        if( !isReach ) {
            resp.code = 1; resp.desc = 'not reach';
        }

    }while(false);

    onReqHandled(res, resp);
};

// 幸运号码
function LuckyNumber() {
    this.users = {                  // 参加本次抽奖活动的玩家信息 
        /*
        uid : {
            name : '',
            num: 0,         
        }
        */
    };
    
    this.luckyNumber = -1;          // 每期开奖幸运号码[0-999],-1 表示未开奖
    this.openDay = 0;               // 本期幸运号码开奖日期
    // 内存变量
    this.selectNumbers = {          // 本期号码选择分布情况
        /*number : count*/
    };
    
    this.wins = [                   // 中奖记录
        /*{uid:name}*/              // 玩家UID:玩家姓名
    ];

    this.updates = {};
}

LuckyNumber.create = function() {
    var luckyNumber = {
        _id: 'lucky_number', 
        user : {},
        lucky_number : -1,      
        open_day : 0,   
    };

    gDBWorld.insert(luckyNumber, function(err, result){});
}

LuckyNumber.prototype = {
    init : function(callback) {
        gDBWorld.findOne({_id : 'lucky_number'}, {}, function(err, doc){
            if( doc ) {
                this.users = doc.user;
                this.luckyNumber = doc.lucky_number;
                this.openDay = doc.open_day;
                this._init(callback);
            }else{
                callback && callback(false);
            }
        }.bind(this));
    },

    _init : function(callback) {
        
        var users = this.users;
        var selectNumbers = {};
        for( var uid in users ) {
            var user = users[uid]; 
            var num = user.num;
            if( !(num in selectNumbers) ) {
                selectNumbers[num] = 0;
            } 

            selectNumbers[num] += 1;
        }

        this.selectNumbers = selectNumbers;

        callback && callback(true);
    },


    save : function(callback) {
        if( Object.keys(this.updates).length == 0 ) {
            callback && callback(true);
            return;
        }
        
        var updates = this.updates;
        gDBWorld.update({_id : 'lucky_number'}, {$set:updates}, function(err, result){
            if( err ) {
                ERROR(err);
                ERROR(updates);
                callback && callback(false);
            }else {
                callback && callback(true);
            }
        });

        this.updates = {};
    },

    addUpdate : function(key, value) {
        this.updates[key] = value;
    },
    
    addUser : function(uid, name, num) {

        var user = {
            'num' : num,
            'name' : name,
        };

        this.users[uid] = user;
        this.addUpdate(util.format('user.%d', uid), user);
        
        if( !(num in this.selectNumbers) ) {
            this.selectNumbers[num] = 0; 
        }

        this.selectNumbers[num] += 1;
    },

    getUser : function(uid) {
        return this.users[uid] || null; 
    },
    
    isSelectPeriod : function() {
        // 是否为选号时间
        var now = common.getTime();
        var todayDate = new Date();
        var todayStartTime = +(new Date(todayDate.getFullYear(), 
            todayDate.getMonth(), todayDate.getDate(), 0, 0, 0))/1000;
        var openStartTime = todayStartTime + gConfGlobal.LuckyNumberSelectEndHour * 3600;
        return (now < openStartTime) ? true : false; 
    },

    updateLuckyNumber : function() {
        var today = common.getDate(); 
        if( today != this.openDay  ) {
            // 清除上一次开奖信息 
            this.users = {};
            this.luckyNumber = -1;
            this.openDay = today;
            this.selectNumbers = {};
            this.wins = [];
            this.updates = {};
            
            this.addUpdate('user', {});
            this.addUpdate('lucky_number', -1);
            this.addUpdate('open_day', today);
        }
        
        if( this.isSelectPeriod() || (Object.keys(this.users).length <= 0) ||
            (this.luckyNumber >= 0) ) {
            return; 
        }
        
        // 开奖
        var selectNumbers = this.selectNumbers;
        var minCount = 0;
        var luckyNumber = -1;
        for( var num in selectNumbers ) {
            if( 0 == minCount ) {
                minCount = selectNumbers[num];
                luckyNumber = num;
                continue;
            }

            if( selectNumbers[num] < minCount ) {
                minCount = selectNumbers[num];
                luckyNumber = num;
            }
        }

        luckyNumber = +luckyNumber;
        if( luckyNumber < 0 ) {
            return; 
        }
        
        this.luckyNumber = luckyNumber;
        this.addUpdate('lucky_number', luckyNumber);
    },

    getMessage : function() {
        if( this.luckyNumber < 0 ) return [];

        if( this.wins.length > 0 ) return this.wins;
        
        var users = this.users;
        var wins = [];
        for( var id in users ) {
            var user = users[id]; 
            if( user.num != this.luckyNumber ) continue;

            var win = {};
            win[id] = user.name;
            wins.push(win);
        } 

        this.wins = wins;

        return wins.slice(0,300);
    },

}

exports.get_lucky_number = function(req, res, resp) {
    var uid = req.uid;
    do {
        gLuckyNumber.updateLuckyNumber(); 
        
        var user = gLuckyNumber.getUser(uid);
        
        resp.data.lucky_number = gLuckyNumber.luckyNumber; 
        resp.data.my_number = (user ? user.num : -1);
        resp.data.messages = gLuckyNumber.getMessage(); 

    } while(false);

    gLuckyNumber.save();
    onReqHandled(res, resp);
};

exports.select_lucky_number = function(req, res, resp) {
    var uid = req.uid;
    do {
        if( !gLuckyNumber.isSelectPeriod() || gLuckyNumber.luckyNumber >=0 ) {
            resp.code = 1; resp.desc = 'not in select period';break;
        }
        
        var user = gLuckyNumber.getUser(uid);
        if( user ) {
            gLuckyNumber.selectNumbers[user.num] -= 1;
        }

        var selectNum = Math.floor(req.args.num); 
        gLuckyNumber.addUser(uid, req.args.name, Math.floor(req.args.num));

    } while(false);
    
    gLuckyNumber.save();
    onReqHandled(res, resp);
};

exports.lucky_number_reward = function(req, res, resp) {
    var uid = req.uid;
    do {
        var user = gLuckyNumber.getUser(uid); 
        if( !user ) {
            resp.code = 1; resp.desc = 'no user';break;
        }

        gLuckyNumber.updateLuckyNumber();
        if( gLuckyNumber.luckyNumber < 0 ) {
            resp.code = 1; resp.desc = 'no reward';break;
        }
        
        var rewardType = 'join';
        if( user.num == gLuckyNumber.luckyNumber ) {
            var activitiesConf = gConfActivities.luckynumber;
            var passedDay = common.getDayPassed(activitiesConf.StartTime);
            if( !gConfAvLuckyNumber[passedDay] ) {
                resp.code = 1; resp.desc = 'not reward at today';break;
            }

            rewardType = passedDay;
        }
        
        var avLuckyNumberConf = gConfAvLuckyNumber[rewardType];
        resp.data.awards = [ avLuckyNumberConf.Award1, avLuckyNumberConf.Award2, avLuckyNumberConf.Award3,];
    } while(false);

    gLuckyNumber.save();
    onReqHandled(res, resp);
};

exports.Activity = Activity;
exports.LuckyNumber = LuckyNumber;
